#include "SQLCleanUp.h"
#include "IsapiTools.h"


const char * const VARSSTR = "([^?&=]+)=([^&]+)";
const char * const URLSTR = "(\\bor\\b)|(\\d)(or)([^a-zA-Z])|([']{1})|([;]{1})";
//const char * const SAFEFORMAT = "(?1*or*)(?2\\2)(?3*or*)(?4\\4)(?5'')(?6,)";
const char * const SAFEFORMAT = "(?1*or*)(?2\\2)(?3*or*)(?4\\4)(?5\\'')(?6,)";
const string IGNOREVARS[] = { "__VIEWSTATE", "__EVENTTARGET", "__EVENTARGUMENT", "__EVENTVALIDATION" };

CSQLCleanUp::CSQLCleanUp(void)
{
	
}

CSQLCleanUp::~CSQLCleanUp(void)
{
}

bool CSQLCleanUp::ShouldIgnoreVar(const string & Var)
{
	for(int i=0;IGNOREVARS[i] != "\0"; i++)
	{
		if(Var == IGNOREVARS[i])
			return true;
	}
	return false;
}

void CSQLCleanUp::CleanURLVar(string URLString)
{
	// Split URLString and clean up only
	
	reg.assign(VARSSTR);

	std::string::const_iterator start, end;



	start = URLString.begin();
	end = URLString.end();
	StrippedURL = "";

	while(boost::regex_search(start, end, matches, reg, boost::match_default ))
	{
		string varName, varContent;
		
		varName.assign(matches[1].first, matches[1].second);
		varContent.assign(matches[2].first, matches[2].second);
		
		string unescaped;

        if(!ShouldIgnoreVar(varName))
		{
			unescaped = UriDecode(varContent);
			unescaped = StripUnsafeSequences(unescaped);
			varContent = UriEncode(unescaped);
		}
		start = matches[0].second;
		if(StrippedURL.size() > 0)
		{
			StrippedURL += "&"+varName+"=";
		} else
		{
			StrippedURL += varName+"=";
		}

		StrippedURL.append(varContent);


	}
	;
	
	return;


}

string CSQLCleanUp::StripUnsafeSequences(const std::string& UnsafeString)
{
   urlExp.assign(URLSTR, boost::regex::icase);
   string safeFormat(SAFEFORMAT);
   return boost::regex_replace(UnsafeString, urlExp, safeFormat, boost::match_default | boost::format_all);

}

const char HEX2DEC[256] = 
{
    /*       0  1  2  3   4  5  6  7   8  9  A  B   C  D  E  F */
    /* 0 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* 1 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* 2 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* 3 */  0, 1, 2, 3,  4, 5, 6, 7,  8, 9,-1,-1, -1,-1,-1,-1,
    
    /* 4 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* 5 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* 6 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* 7 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    
    /* 8 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* 9 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* A */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* B */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    
    /* C */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* D */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* E */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* F */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1
};
    
string CSQLCleanUp::UriDecode(const string & sSrc)
{
    // Note from RFC1630:  "Sequences which start with a percent sign
    // but are not followed by two hexadecimal characters (0-9, A-F) are reserved
    // for future extension"
    
    const unsigned char * pSrc = (const unsigned char *)sSrc.c_str();
	const int SRC_LEN = sSrc.length();
    const unsigned char * const SRC_END = pSrc + SRC_LEN;
    const unsigned char * const SRC_LAST_DEC = SRC_END - 2;   // last decodable '%' 

    char * const pStart = new char[SRC_LEN];
    char * pEnd = pStart;

    while (pSrc < SRC_LAST_DEC)
	{
		if (*pSrc == '%')
        {
            char dec1, dec2;
            if (-1 != (dec1 = HEX2DEC[*(pSrc + 1)])
                && -1 != (dec2 = HEX2DEC[*(pSrc + 2)]))
            {
                *pEnd++ = (dec1 << 4) + dec2;
                pSrc += 3;
                continue;
            }
        }

        *pEnd++ = *pSrc++;
	}

    // the last 2- chars
    while (pSrc < SRC_END)
        *pEnd++ = *pSrc++;

    std::string sResult(pStart, pEnd);
    delete [] pStart;
	return sResult;
}


string CSQLCleanUp::UriEncode(const std::string & sSrc)
{
    const char DEC2HEX[16 + 1] = "0123456789ABCDEF";
    const unsigned char * pSrc = (const unsigned char *)sSrc.c_str();
    const int SRC_LEN = sSrc.length();
    unsigned char * const pStart = new unsigned char[SRC_LEN * 3];
    unsigned char * pEnd = pStart;
    const unsigned char * const SRC_END = pSrc + SRC_LEN;

    for (; pSrc < SRC_END; ++pSrc)
	{
		if (!ShouldEscape(*pSrc)) 
            *pEnd++ = *pSrc;
        else
        {
            // escape this char
            *pEnd++ = '%';
            *pEnd++ = DEC2HEX[*pSrc >> 4];
            *pEnd++ = DEC2HEX[*pSrc & 0x0F];
        }
	}

    std::string sResult((char *)pStart, (char *)pEnd);
    delete [] pStart;
    return sResult;
}

bool CSQLCleanUp::ShouldEscape(BYTE c)
/*++

Purpose:

    Determines whether the specified character should
    be escape encoded, per RFC2396

Arguments:

    c - The character to consider

Returns:

    TRUE if yes, FALSE if no

--*/
{
    //
    // If the character is listed in RFC2396, section
    // 2.4.3 as control, space, delims or unwise, we
    // should escape it.  Also, we should escape characters
    // with the high bit set.
    //

    if ( c <= 0x1f ||
         c == 0x7f )
    {
        //
        // Control character
        //

        goto ReturnTrue;
    }

    if ( c >= 0x80 )
    {
        //
        // High bit set
        //

        goto ReturnTrue;
    }

    switch ( c )
    {

    //
    // space
    //
    case ' ':

    //
    // delims
    //
    case '<':
    case '>':
    case '#':
    case '%':
    case '\"':

    //
    // unwise
    //
    case '{':
    case '}':
    case '|':
    case '\\':
    case '^':
    case '[':
    case ']':
    case '`':
	case '=':
	case '&':

        goto ReturnTrue;
    }

    //
    // If we get here, then the character should not be
    // escaped
    //

    return FALSE;

ReturnTrue:

    return TRUE;
}

